package belf.migrate.core.exception;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** dbmove global exception, used to tell user more clearly error messages */
public class BelfRuntimeException extends Exception {


    private final BelfErrorCode eErrorCode;
    private Throwable cause = null;
    private final Map<String, String> params;

    public BelfRuntimeException(BelfErrorCode eErrorCode, String errorMessage) {
        super(eErrorCode.getErrorMessage() + " - " + errorMessage);
        this.eErrorCode = eErrorCode;
        this.cause = null;
        this.params = new HashMap<>();
    }

    public BelfRuntimeException(
            BelfErrorCode eErrorCode, String errorMessage, Throwable cause) {
        super(eErrorCode.getErrorMessage() + " - " + errorMessage, cause);
        this.eErrorCode = eErrorCode;
        this.cause = cause;
        this.params = new HashMap<>();

    }

    public BelfRuntimeException(BelfErrorCode eErrorCode, Throwable cause) {
        super(eErrorCode.getErrorMessage(), cause);
        this.eErrorCode = eErrorCode;
        this.cause = cause;
        this.params = new HashMap<>();
    }

    public BelfRuntimeException(
            BelfErrorCode eErrorCode, Map<String, String> params) {
        super(getDescription(eErrorCode.getErrorMessage(), params));
        this.eErrorCode = eErrorCode;
        this.params = params;
    }

    public BelfRuntimeException(
            BelfErrorCode eErrorCode, Map<String, String> params, Throwable cause) {
        super(getDescription(eErrorCode.getErrorMessage(), params),
                cause);
        this.eErrorCode = eErrorCode;
        this.cause = cause;
        this.params = params;
    }

    public BelfErrorCode geteErrorCode() {
        return eErrorCode;
    }

    @Override
    public Throwable getCause() {
        return cause;
    }

    public Map<String, String> getParams() {
        return params;
    }

    public static String getDescription(String descriptionTemplate, Map<String, String> params) {
        String description = descriptionTemplate;
        for (String param : getParams(descriptionTemplate)) {
            String value = params.get(param);
            description = description.replace(String.format("<%s>", param), value);
        }
        return description;
    }

    private static final Pattern PARAMS_PATTERN = Pattern.compile("<([a-zA-Z0-9]+)+>");
    /**
     * Get all params key in description, the param key should be wrapped by <>. eg: "<param1>
     * <param2>" will return ["param1", "param2"]
     *
     * @param description error description
     * @return params key list
     */
    public static List<String> getParams(String description) {
        // find all match params key in description
        Matcher matcher = PARAMS_PATTERN.matcher(description);
        List<String> params = new ArrayList<>();
        while (matcher.find()) {
            String key = matcher.group(1);
            params.add(key);
        }
        return params;
    }
}
